package cn.schoolwow.quickflow.service.flowContext.putData;

import cn.schoolwow.quickflow.QuickFlow;
import cn.schoolwow.quickflow.domain.*;
import cn.schoolwow.quickflow.dto.FlowContextDataRange;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class FlowContextPutDataOperationImpl implements FlowContextPutDataOperation {
    private FlowExecutorConfig flowExecutorConfig;

    public FlowContextPutDataOperationImpl(FlowExecutorConfig flowExecutorConfig) {
        this.flowExecutorConfig = flowExecutorConfig;
    }

    @Override
    public FlowContext putThreadLocalData(String key, Object value, FlowDataFeature... flowDataFeatures) {
        return putFlowContextData(key, value, FlowContextDataRange.ThreadLocal, flowDataFeatures);
    }

    @Override
    public FlowContext putCurrentFlowData(String key, Object value, FlowDataFeature... flowDataFeatures) {
        return putFlowContextData(key, value, FlowContextDataRange.CurrentFlow, flowDataFeatures);
    }

    @Override
    public FlowContext putCurrentCompositeFlowData(String key, Object value, FlowDataFeature... flowDataFeatures) {
        return putFlowContextData(key, value, FlowContextDataRange.CurrentCompositeBusiness, flowDataFeatures);
    }

    @Override
    public FlowContext putTemporaryData(String key, Object value) {
        return putFlowContextData(key, value, FlowContextDataRange.Flow, FlowDataFeature.TemporaryData);
    }

    @Override
    public FlowContext putReturnData(String key, Object value) {
        return putFlowContextData(key, value, FlowContextDataRange.Flow, FlowDataFeature.ReturnData);
    }

    @Override
    public FlowContext putData(String key, Object value, FlowDataFeature... flowDataFeatures) {
        return putFlowContextData(key, value, FlowContextDataRange.Flow, flowDataFeatures);
    }

    @Override
    public FlowContext putContextThreadLocalData(String key, Object value, FlowDataFeature... flowDataFeatures) {
        return putFlowContextData(key, value, FlowContextDataRange.ContextThreadLocal, flowDataFeatures);
    }

    @Override
    public FlowContext putContextTemporaryData(String key, Object value) {
        return putFlowContextData(key, value, FlowContextDataRange.Context, FlowDataFeature.TemporaryData);
    }

    @Override
    public FlowContext putContextData(String key, Object value, FlowDataFeature... flowDataFeatures) {
        return putFlowContextData(key, value, FlowContextDataRange.Context, flowDataFeatures);
    }

    @Override
    public FlowContext putFlowContextData(String key, Object value, FlowContextDataRange flowContextDataRange, FlowDataFeature... flowDataFeatures) {
        FlowContextDataOption flowContextDataOption = new FlowContextDataOption();
        flowContextDataOption.key = key;
        flowContextDataOption.value = value;
        flowContextDataOption.flowContextDataRange = flowContextDataRange;
        flowContextDataOption.flowDataFeatureList = Arrays.asList(flowDataFeatures);
        return putFlowContextData(flowContextDataOption);
    }

    @Override
    public FlowContext putFlowContextDataMap(Map<String, Object> dataMap, FlowContextDataRange flowContextDataRange, FlowDataFeature... flowDataFeatures) {
        Set<String> keySet = dataMap.keySet();
        for(String key:keySet){
            Object value = dataMap.get(key);
            putFlowContextData(key, value, flowContextDataRange, flowDataFeatures);
        }
        return flowExecutorConfig.flowContext;
    }

    @Override
    public FlowContext putFlowContextData(FlowContextDataOption flowContextDataOption) {
        if(null==flowContextDataOption.flowContextDataRange){
            throw new IllegalArgumentException("上下文数据范围不能为空");
        }
        if(null==flowContextDataOption.key||null==flowContextDataOption.value){
            return flowExecutorConfig.flowContext;
        }
        FlowDataFeature[] flowDataFeatures = flowContextDataOption.flowDataFeatureList.toArray(new FlowDataFeature[0]);
        boolean skipExistData = FlowDataFeature.containFeature(FlowDataFeature.SkipExistData, flowDataFeatures);
        boolean temporaryData = FlowDataFeature.containFeature(FlowDataFeature.TemporaryData, flowDataFeatures);
        boolean requestData = FlowDataFeature.containFeature(FlowDataFeature.RequestData, flowDataFeatures);
        boolean returnData = FlowDataFeature.containFeature(FlowDataFeature.ReturnData, flowDataFeatures);
        boolean flowConfigData = FlowDataFeature.containFeature(FlowDataFeature.FlowConfigData, flowDataFeatures);

        String key = flowContextDataOption.key;
        Object value = flowContextDataOption.value;

        switch (flowContextDataOption.flowContextDataRange){
            case Context:{
                FlowConfig flowConfig = flowExecutorConfig.flowExecutorRootConfig.flowConfig;
                if(skipExistData){
                    flowConfig.flowConfigDataContext.dataMap.putIfAbsent(key, value);
                }else{
                    flowConfig.flowConfigDataContext.dataMap.put(key, value);
                }
                if(temporaryData){
                    flowConfig.flowConfigDataContext.flowContextDataKeySet.temporaryDataKeySet.add(key);
                }
            }break;
            case ContextThreadLocal:{
                FlowConfig flowConfig = flowExecutorConfig.flowExecutorRootConfig.flowConfig;
                ThreadLocal<Map<String,Object>> threadLocal = flowConfig.flowConfigDataContext.threadLocalDataMap;
                if(null==threadLocal.get()){
                    threadLocal.set(new HashMap<>());
                }
                Map<String,Object> contextThreadLocalMap = threadLocal.get();
                if(skipExistData){
                    contextThreadLocalMap.putIfAbsent(key, value);
                }else{
                    contextThreadLocalMap.put(key, value);
                }
                if(temporaryData){
                    flowConfig.flowConfigDataContext.flowContextDataKeySet.temporaryDataKeySet.add(key);
                }
            }break;
            case Flow:{
                if(skipExistData){
                    flowExecutorConfig.flowExecutorRootConfig.dataMap.putIfAbsent(key, value);
                }else{
                    flowExecutorConfig.flowExecutorRootConfig.dataMap.put(key, value);
                }
                flowExecutorConfig.flowExecutorRootConfig.dataMap.put(key, value);
                if(requestData){
                    flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.requestDataKeySet.add(key);
                }
                if(temporaryData){
                    flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.temporaryDataKeySet.add(key);
                }
                if(returnData){
                    flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.returnDataKeySet.add(key);
                }
                if(flowConfigData){
                    flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.flowConfigDataKeySet.add(key);
                }
            }break;
            case CurrentCompositeBusiness:{
                Map<String,Object> currentCompositeFlowDataMap = flowExecutorConfig.currentCompositeFlowDataMap;
                if(skipExistData){
                    currentCompositeFlowDataMap.putIfAbsent(key, value);
                }else{
                    currentCompositeFlowDataMap.put(key, value);
                }
                if(requestData){
                    flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.requestDataKeySet.add(key);
                }
                if(temporaryData){
                    flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.temporaryDataKeySet.add(key);
                }
                if(returnData){
                    flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.returnDataKeySet.add(key);
                }
            }break;
            case CurrentFlow:{
                Map<String,Object> currentFlowDataMap = flowExecutorConfig.currentFlowDataMap;
                if(skipExistData){
                    currentFlowDataMap.putIfAbsent(key, value);
                }else{
                    currentFlowDataMap.put(key, value);
                }
                currentFlowDataMap.put(key, value);
            }break;
            case ThreadLocal:{
                Map threadLocalMap = flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.get();
                if(null==threadLocalMap){
                    flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.set(new HashMap<>());
                }
                threadLocalMap = flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.get();
                threadLocalMap.put(key, value);
            }break;
        }
        return flowExecutorConfig.flowContext;
    }

    @Override
    public FlowContext removeData(String key) {
        flowExecutorConfig.flowExecutorRootConfig.dataMap.remove(key);
        return flowExecutorConfig.flowContext;
    }

    @Override
    public FlowContext removeData(String key, FlowContextDataRange flowContextDataRange) {
        if(null==flowContextDataRange){
            throw new IllegalArgumentException("上下文数据范围不能为空");
        }
        switch (flowContextDataRange){
            case Context:{
                FlowConfig flowConfig = flowExecutorConfig.flowExecutorRootConfig.flowConfig;
                flowConfig.flowConfigDataContext.dataMap.remove(key);
            }break;
            case ContextThreadLocal:{
                FlowConfig flowConfig = flowExecutorConfig.flowExecutorRootConfig.flowConfig;
                ThreadLocal<Map<String,Object>> threadLocal = flowConfig.flowConfigDataContext.threadLocalDataMap;
                if(null==threadLocal.get()){
                    threadLocal.set(new HashMap<>());
                }
                Map<String,Object> contextThreadLocalMap = threadLocal.get();
                contextThreadLocalMap.remove(key);
            }break;
            case Flow:{
                flowExecutorConfig.flowExecutorRootConfig.dataMap.remove(key);
            }break;
            case CurrentCompositeBusiness:{
                Map<String,Object> currentCompositeFlowDataMap = flowExecutorConfig.currentCompositeFlowDataMap;
                currentCompositeFlowDataMap.remove(key);
            }break;
            case CurrentFlow:{
                Map<String,Object> currentFlowDataMap = flowExecutorConfig.currentFlowDataMap;
                currentFlowDataMap.remove(key);
            }break;
            case ThreadLocal:{
                Map threadLocalMap = flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.get();
                if(null==threadLocalMap){
                    flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.set(new HashMap<>());
                }
                threadLocalMap = flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.get();
                threadLocalMap.remove(key);
            }break;
        }
        return flowExecutorConfig.flowContext;
    }

    @Override
    public FlowContext joinQuickFlow(QuickFlow quickFlow) {
        Map<String,Object> contextDataMap = quickFlow.getContextDataMap();
        for(String key:contextDataMap.keySet()){
            putData(key, contextDataMap.get(key));
        }
        Set<String> temporaryDataKeySet = quickFlow.getFlowConfig().flowConfigDataContext.flowContextDataKeySet.temporaryDataKeySet;
        flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.temporaryDataKeySet.addAll(temporaryDataKeySet);
        return flowExecutorConfig.flowContext;
    }

}
